// Author: Bjorn Schobben

#include <fstream>
#include <iostream>
#include <sstream>

#include "Map.h"

using namespace std;

Map::Map()
{
}

Map::Map(const Map& rhs) 
{
	Copy(rhs);
}

Map::~Map()
{
	Clear();
}

// Copy a map by assignment
Map& Map::operator=(const Map& rhs)
{
	if (this != &rhs)
	{
		Clear();
		Copy(rhs);
	}

	return *this;
}

// Copy a map by inserting it one-by-one into this map
void Map::Copy(const Map& rhs)
{
	std::list<Land*>::const_iterator landIt;

	for(landIt = rhs.lands.begin(); landIt != rhs.lands.end(); ++landIt)
	{
		const Land* land = *landIt;

		InsertNewLand(*land);
	}

	// Update neighbours 
	UpdateNeighbours();
}

// Clear map contents, clean up
void Map::Clear()
{
	std::list<Land*>::iterator it;
	for(it = lands.begin(); it != lands.end(); ++it)
	{
		delete *it;
	}

	lands.clear();
}

std::list<Land*>& Map::GetLands()
{
	return lands;
}

Land* Map::InsertLand(Land* land)
{
	lands.push_back(land);

	return lands.back();
}

Land* Map::InsertNewLand(const std::string& name)
{
	return InsertLand(new Land(name));
}

Land* Map::InsertNewLand(const Land& land)
{
	return InsertLand(new Land(land));
}

// Update the list of neighbours for each province
void Map::UpdateNeighbours()
{
	std::list<Land*>::iterator it;
	std::list<Land*>::const_iterator neighbourIt;

	for(it = lands.begin(); it != lands.end(); ++it)
	{
		Land *land = *it;

		// Clear all neighbours of the land
		land->ClearNeighbours();

		// Update the list of neighbours for a lands own provinces
		land->UpdateNeighbours(0);

		// Update neighbours by cross matching this land with the other lands
		for(neighbourIt = lands.begin(); neighbourIt != lands.end(); ++neighbourIt)
		{
			const Land* neighbourLand = *neighbourIt;

			// If this land is the land we are inspecting, skip it
			if (land == neighbourLand)
				continue;
			
			land->UpdateNeighbours(neighbourLand);
		}
	}
}

// Get the unique color count used in this map
unsigned int Map::GetUniqueColorCount() const
{
	std::list<Color> colorTable;
	std::list<Land*>::const_iterator it;

	// Accumulate all land's colors
	for(it = lands.begin(); it != lands.end(); ++it)
	{
		const Land *land = *it;

		land->GetUniqueColors(colorTable);
	}

	return (unsigned int)colorTable.size();
}

// Return if map is colored according to the 4-color problem
bool Map::IsCompleted() const
{
	bool result = true;
	std::list<Land*>::const_iterator it;


	// Ask all lands if they are completed
	for(it = lands.begin(); it != lands.end(); ++it)
	{
		const Land *land = *it;

		// Ask land if its completed
		if (!land->IsCompleted())
		{
			result = false;
			break;
		}
	}

	return result;
}

bool Map::SetColor(const std::string& landName, const std::string& provinceName, Color color)
{
	Land *land = GetLand(landName);
	bool result = false;

	// Set a province color
	if (land)
	{
		// via its land
		result = land->SetColor(provinceName, color);
	}

	return result;
}

// Get pointer to a specific land
Land* Map::GetLand(const std::string& landName)
{
	std::list<Land*>::iterator it;
	Land *result = 0;


	for(it = lands.begin(); it != lands.end(); ++it)
	{
		Land* land = *it;

		// If names match
		if (land->GetName() == landName)
		{
			// then return its pointer
			result = land;
			break;
		}
	}

	return result;
}

// Same as above, but constant pointer now
const Land* Map::GetLand(const std::string& landName) const
{
	std::list<Land*>::const_iterator it;
	const Land *result = 0;


	for(it = lands.begin(); it != lands.end(); ++it)
	{
		const Land* land = *it;

		if (land->GetName() == landName)
		{
			result = land;
			break;
		}
	}

	return result;
}

bool Map::ProcessCoordinates(Province* province, const std::string& coordinates) const
{
	bool result = false;

	// Make sure the province exists
	if (province)
	{
		std::string spaceCoordinates = coordinates;
		// Change every comma to space
		for(unsigned int i=0; i<spaceCoordinates.length(); i++)
		{
			if (spaceCoordinates[i] == ',')
				spaceCoordinates[i] = ' ';
		}

		stringstream s(spaceCoordinates);

		// Keep adding coordinates
		while(!s.eof())
		{
			int x, y;
			s >> x >> y;

			province->InsertNewPoint(x, y);
		}

		result = true;
	}

	return result;
}

bool Map::ProcessLine(Land **land, const std::string& line)
{
	bool result = false;
	stringstream s(line);
	string word;

	// Extract the first word 
	s >> word;

	// If the line is empty now
	if (s.eof())
	{
		// Then it's a new land, so make a new one
		*land = InsertNewLand(word);

		if (*land)
		{
			result = true;
		}
	}
	else if (*land)// Else it is a province, make sure land exists
	{
		int pointCount;
		// Get the number of points in the province
		s >> pointCount;

		// Insert the new province in the land
		Province *province = (*land)->InsertNewProvince(word);

		// Get a string full with coordinates for this province
		string coordinates;
		getline(s, coordinates);

		result = ProcessCoordinates(province, coordinates);
	}

	return result;
}

// Load a map from a file
bool Map::LoadFromFile(const std::string& filename)
{
	Land* land = 0;
	bool result = false;
	string line;

	ifstream f( filename.c_str() );

	// Make map empty first
	Clear();
	
	// Start reading the map
	while( f.good() && !f.eof() )
	{
		// Read a line from the map file
		getline(f, line);

		// Check for errors
		if (f.bad() || f.fail())
			break;

		// Skip empty lines
		if (0 == line.length())
			continue;

		result = ProcessLine(&land, line);

		if (!result)
			break;
	}

	// If error occurred
	if (!result)
		Clear();

	// Close file
	f.close();

	// if successful
	if (result)
		UpdateNeighbours();

	return result;
}

